#include "pvm3.h"
#include "constantes.h"
#include "rejilla.h"
#include <cstdio>
#include <cstdlib>

Rejilla*	vector;          /**< Vector de rejillas que contiene los distintos sectores */
Rejilla*	grid;            /**< Rejilla que se crea cuando cargamos el fichero */
Rejilla*	final;           /**< Rejilla que sirve para almacenar los sectores que llegan de los hijos */
int			ciclos = 0;      /**< Numero de ciclos que queremos que ejecute el programa */ 
int			tamSector = 3;   /**< Dimensiones de los sectores que pasamos a los hijos */
int			ejecutados = 0;  /**< Numero de ciclos que se han ejecutado ya */
int			partes = 0;      /**< Numero de partes en que se divide la rejilla, expandiremos un hijo por cada parte */
int         numex = 0;       /**< Numero de hijos expandidos con exito */ 
int*        tids;            /**< Vector con el numero de proceso de cada hijo */
int			tPunto;          /**< Tamaño del punto para la representacion grafica */

/**
 *Este es el metodo principal del programa. Se encarga de gestionar todas las opciones que
 *introduce el usuario, poner en marcha los procesos que calcularan los diferentes estados
 *del juego y gestionar las llamadas a otros subprocesos que controlan la interfaz grafica
 *o cualquier otro aspecto del programa.
 *@author Roman Jimenez de Andres
 *@author Luis Ciudad Garcia
 *@date 02/09/2009
 *@param argc Numero de parametros que recibe el programa.
 *@param argv Vector de cadenas con cada uno de los parametros que recibe el programa.
 */
int main(int argc, char **argv){
	struct timeval t0, t1;
    double tej;

    gettimeofday(&t0,0);

    string fichero =     ""; /**< Nombre del fichero desde el que se carga la rejilla */ 
    int opcion =         0;  /**< Esta variable contiene el numero de caracter de las opciones de parametros por consola */
    int opcion_fichero = 0;  /**< Esta variable indica si el usuario quiere salida por fichero al finalizar */

    /**
     *Este bucle va capturando una a una las distintas opciones que ha introducido el usuario
     *de formas que podamos poner las variables a sus valores correspondientes
     */
    while ((opcion = getopt (argc, argv, "f:c:s::t:")) != -1){
        switch (opcion){
            case 'f':
                fichero = optarg;
                break;
            case 'c':
                ciclos = atoi(optarg);
                break;
            case 's':
                opcion_fichero = 1;
                break;
            case 't':
                tamSector = atoi(optarg);
                break;
            default:
                cout << "Parametro no reconocido" << endl;
            }
        }

    /** 
     *Una vez que tenemos todos los parametros, comprobamos que son correctos,
     *en caso contrario mostramos un mensaje sobre el funcionamiento del programa
     *y salimos
     */
    if(!ciclos || ciclos <= 0 || fichero == "" || tamSector <= 2){    
        cout << "Uso: ./mainlite -f <fichero de entrada> -c <numero de cilos> [-t <tamano de sector (min 3)>]" << endl;
        return 1;
        }

    //pvm_catchout(stdout);

    /** 
     *Si todo ha ido bien ya podemos cargar la rejilla desde el fichero. Ademas creamos la 
     *rejilla que albergara las partes que vayan llegando de los hijos. Esta rejilla puede que
     *sea mas grande que la original dependiendo de las dimensiones de los sectores.
     */
    grid = new Rejilla(fichero, tamSector);
	final = new Rejilla(tamSector*grid->numPartesAlto(),tamSector*grid->numPartesAncho(), tamSector);
    
	/** Creamos las partes que pasaremos a cada hijo */
    vector = grid->particiones(partes);

    /** Expandimos los procesos que calcularan cada sector de la rejilla */
    tids = new int[partes];
    numex = pvm_spawn((char *)HIJOLITE, (char **)0, 0, 0, partes, tids);
    
    /** Si no se han expandido todos los hijos devolvemos un error */
    if(numex != partes || partes == 0){
        cout << "Error: No se expandieron todos los hijos" << endl;
        return 1;
        }	

    /** A continuacion, por cada hijo creado debemos pasar los parametros correspondientes */
    for(int i = 0; i < partes; i++){

        int *matriz = vector[i].getMatriz();

        int vecinos[9] = {-1,-1,-1,-1,-1,-1,-1,-1,-1};
        
        /** 
         *El siguiente calculo nos permite pasarle una referencia a cada hijo sobre los hermanos 
         *a los que debe perdirle un sector ya que las celdas de los bordes del sector propio estan
         *afectados por celdas de los sectores vecinos. Un -1 indica que no tiene vecino en una posicion
         *concreta, de otra manera se pasa el tid del proceso hermano.
         */ 
        int fila = i / (grid->numPartesAncho());
        int columna = i % (grid->numPartesAncho());
        
        int cont = 0;
        for(int k = -1; k <= 1; k++){
            for(int z = -1; z <= 1; z++){
                if((fila+k    >= 0 && fila+k    <= grid->numPartesAlto()  - 1) && 
                   (columna+z >= 0 && columna+z <= grid->numPartesAncho() - 1) && 
                   !(z == 0 && k == 0)){
                    vecinos[cont] = tids[((fila+k)*grid->numPartesAncho())+columna+z];
                    }
                else
                    {
                    vecinos[cont] = -1;
                    }
                cont++;
                }
            }
        
        /** 
          *A cada hijo le pasamos:
          * -El número de nodos total
          * -Las dimensiones de los sectores
          * -Su identificador de sector
          * -Un vector con el tid de los sectores que necesitara
          * -La matriz que representa el sector que tiene que calcular
          * -El numero de ciclos a ejecutar
          * -La opcion de volcado
          * -Los tids de todos los hermanos
          * -El numero de sectores a lo ancho
          * -El numero de sectores a lo largo
          */
        pvm_initsend(PvmDataDefault);
            pvm_pkint(&partes,1,1);
            pvm_pkint(&tamSector,1,1);
            pvm_pkint(&i,1,1);
            pvm_pkint(&vecinos[0],9,1);
            pvm_pkint(&matriz[0],tamSector*tamSector,1);
            pvm_pkint(&ciclos,1,1);
            pvm_pkint(&opcion_fichero,1,1);
            pvm_pkint(tids,partes,1);
            int ancho = grid->numPartesAncho();
            pvm_pkint(&ancho,1,1);
            int alto = grid->numPartesAlto();
            pvm_pkint(&alto,1,1);
            
            
        pvm_send(tids[i],TICKET_INFO);
        }

    /**
     *Esperamos a que cada uno de los hijos
     *nos mande su parte, cuando recibimos cada parte la colocamos en el lugar que
     *le corresponde.
     */
    for(int i = 0; i < partes; i++){
        pvm_recv(-1, TICKET_FINAL);
            int sector = -1;
            int nuevoEstado[tamSector*tamSector];
            pvm_upkint(&sector,1,1);
            pvm_upkint(&nuevoEstado[0],tamSector*tamSector,1);
            int fila = sector / (grid->numPartesAncho());
            int columna = sector % (grid->numPartesAncho());

        final->setSector(fila*tamSector,columna*tamSector,nuevoEstado);
        }

    gettimeofday(&t1,0);

    tej = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6;

    cout << tej << endl;    

    pvm_exit();
	return 1;
	}


